/* 
 * Copyright (c) 2010 Adam Markiewicz
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @file containers/fixedstack.h
 * @author Adam Markiewicz
 */

#ifndef __CLEA_CONTAINERS_FIXEDSTACK
#define __CLEA_CONTAINERS_FIXEDSTACK

#include <config.h>
#include <core/memory/standardallocator.h>
#include <meta/traits.h>
#include <containers/constructpolicy.h>
#include <containers/destroypolicy.h>
#include <containers/copypolicy.h>
#include <containers/comparepolicy.h>

namespace Clea
{
    namespace Containers
    {
        template <typename Type, typename Allocator = Core::Memory::StandardAllocator>
        class FixedStack
        {
            private:
                // Internal types
                typedef typename Clea::Meta::Traits<Type>::value_type value_type;
                typedef typename Clea::Meta::Traits<Type>::pointer pointer;
                typedef typename Clea::Meta::Traits<Type>::const_pointer const_pointer;
                typedef typename Clea::Meta::Traits<Type>::reference reference;
                typedef typename Clea::Meta::Traits<Type>::const_reference const_reference;
                
                typedef typename Clea::Core::size_type size_type;
                typedef typename Clea::Core::difference_type difference_type;
                
                // Used to simplify notation
                typedef FixedStack<Type, Allocator> Container;
                
            public:
                FixedStack(const size_type size):
                    m_bottom(static_cast<pointer>(Allocator::allocate(size * sizeof(value_type)))),
                    m_top(m_bottom),
                    m_memoryTop(m_bottom + size)
                {}
                
                ~FixedStack() {
                    clear();
                    Allocator::deallocate(m_bottom, (m_memoryTop - m_bottom) * sizeof(value_type));
                }
                
                /**
                 * @brief Current size of the stack
                 */
                size_type size() const {
                    return m_top - m_bottom + 1;
                }
                
                /**
                 * @brief Capacity of the stack
                 */
                size_type capacity() const {
                    return m_memoryTop - m_bottom;
                }
                
                void setCapacity(const size_type size) {
                    if (size != m_memoryTop - m_bottom) {
                        if (!Allocator::resize(m_bottom, (m_memoryTop - m_bottom) * sizeof(value_type), size * sizeof(value_type))) {
                            pointer newBottom = Allocator::allocate(size * sizeof(value_type));
                            CopyPolicy<value_type>::copy(newBottom, m_bottom, (m_top - m_bottom + 1));
                            Allocator::dealocate(m_bottom, (m_memoryTop - m_bottom) * sizeof(value_type));
                            m_top = newBottom + (m_top - m_bottom);
                            m_bottom = newBottom;
                        }
                        m_memoryTop = m_bottom + size;
                    }
                }
                
                void clear() {
                    while (m_top >= m_bottom) {
                        pop();
                    }
                }
                
                reference top() {
                    return *m_top;
                }
                
                const_reference top() const {
                    return *m_top;
                }
                
                void push(const_reference value) {
                    m_top++;
                    ConstructPolicy<value_type>::construct(m_top, value);
                }
                
                void pop() {
                    DestroyPolicy<value_type>::destroy(m_top);
                    m_top--;
                }
                
            protected:
                pointer m_bottom;
                pointer m_top;
                pointer m_memoryTop;
            
        };
    }
}

#endif // __CLEA_CONTAINERS_FIXEDSTACK
